package yawning.mew.equipment;

import java.io.StringReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import yawning.mew.equipment.RawItem.Property;
import yawning.mew.util.URLFetcher;

@SuppressWarnings("serial")
public class WowheadItemSource extends ItemSource {
	private static Map<String,Property> sPropertyMap;
	private static Map<String,Property> sSocketMap;

	static {
		sSocketMap = new HashMap<String,Property>();

		sSocketMap.put("Stamina", Property.STAMINA);
		sSocketMap.put("Agility", Property.AGILITY);
		sSocketMap.put("Strength", Property.STRENGTH);
		sSocketMap.put("Intellect", Property.INTELLECT);
		sSocketMap.put("Spirit", Property.SPIRIT);
		sSocketMap.put("Attack Power", Property.ATTACK_POWER);
		sSocketMap.put("Critical Strike Rating", Property.CRIT_RATING);
		sSocketMap.put("Dodge Rating", Property.DODGE_RATING);
		sSocketMap.put("Expertise Rating", Property.EXPERTISE_RATING);
		sSocketMap.put("Hit Rating", Property.HIT_RATING);
		sSocketMap.put("Haste Rating", Property.HASTE_RATING);
		sSocketMap.put("Mastery Rating", Property.MASTERY_RATING);
		sSocketMap.put("Parry Rating", Property.PARRY_RATING);
		sSocketMap.put("Resilience Rating", Property.RESILIENCE_RATING);

		sPropertyMap = new HashMap<String,Property>();

		sPropertyMap.put("slotbak", Property.SLOT);
		sPropertyMap.put("armor", Property.ARMOR);
		sPropertyMap.put("itemset", Property.ITEM_SET);
		sPropertyMap.put("level", Property.ITEM_LEVEL);

		sPropertyMap.put("sta", Property.STAMINA);
		sPropertyMap.put("agi", Property.AGILITY);
		sPropertyMap.put("str", Property.STRENGTH);
		sPropertyMap.put("int", Property.INTELLECT);
		sPropertyMap.put("spi", Property.SPIRIT);
		sPropertyMap.put("hastertng", Property.HASTE_RATING);
		sPropertyMap.put("critstrkrtng", Property.CRIT_RATING);
		sPropertyMap.put("hitrtng", Property.HIT_RATING);
		sPropertyMap.put("exprtng", Property.EXPERTISE_RATING);
		sPropertyMap.put("atkpwr", Property.ATTACK_POWER);
		sPropertyMap.put("resirtng", Property.RESILIENCE_RATING);
		sPropertyMap.put("dodgertng", Property.DODGE_RATING);
		sPropertyMap.put("mastrtng", Property.MASTERY_RATING);
		sPropertyMap.put("parryrtng", Property.PARRY_RATING);

		sPropertyMap.put("nsockets", Property.NUM_SOCKETS);
		sPropertyMap.put("socket1", Property.SOCKET1);
		sPropertyMap.put("socket2", Property.SOCKET2);
		sPropertyMap.put("socket3", Property.SOCKET3);
		sPropertyMap.put("socketbonus", null);

		sPropertyMap.put("mledmgmin", Property.MELEE_DMG_MIN);
		sPropertyMap.put("mledmgmax", Property.MELEE_DMG_MAX);
		sPropertyMap.put("mledps", Property.MELEE_DPS);
		sPropertyMap.put("mlespeed", Property.MELEE_SPEED);

		sPropertyMap.put("rgddmgmax", Property.RANGED_DMG_MAX);
		sPropertyMap.put("rgddmgmin", Property.RANGED_DMG_MIN);
		sPropertyMap.put("rgddps", Property.RANGED_DPS);
		sPropertyMap.put("rgdspeed", Property.RANGED_SPEED);

		sPropertyMap.put("splpwr", Property.SPELL_POWER);

		// Ignored properties from wowhead
		sPropertyMap.put("sellprice", null);
		sPropertyMap.put("displayid", null);
		sPropertyMap.put("maxcount", null);
		sPropertyMap.put("feratkpwr", null);
		sPropertyMap.put("dura", null);
		sPropertyMap.put("avgbuyout", null);
		sPropertyMap.put("buyprice", null);
		sPropertyMap.put("cooldown", null);

		// Will these every be different from the MELEE_* ones?
		sPropertyMap.put("dps", null);
		sPropertyMap.put("dmgmin1", null);
		sPropertyMap.put("dmgmax1", null);
		sPropertyMap.put("dmgtype1", null);
		sPropertyMap.put("speed", null);

		// Maybe needed for requirements check
		sPropertyMap.put("reqskillrank", null);
		sPropertyMap.put("reqskill", null);
		sPropertyMap.put("reqrep", null);
		sPropertyMap.put("reqlevel", null);
		sPropertyMap.put("classes", null);
		sPropertyMap.put("reqfaction", null);
		sPropertyMap.put("reqarenartng", null);
		sPropertyMap.put("races", null);
	}


	private static Map<Integer,Map<Property,Number>> sQuirkTable;

	static {
		sQuirkTable = new HashMap<Integer,Map<Property,Number>>();

		// Ranseur of Hatred (391)
		sQuirkTable.put(71557, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 3628);
		}});

		// Ruthless Gladaitor's Pike (391)
		sQuirkTable.put(70182, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 3628);
		}});

		// Ranseur of Hatred (378)
		sQuirkTable.put(71361, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 3214);
		}});

		// Ruthless Gladiator's Pike (378)
		sQuirkTable.put(70225, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 3214);
		}});

		// Malevolence (372)
		sQuirkTable.put(65139, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 2026);
		}});

		// Witch-Hunter's Harvester
		sQuirkTable.put(70165, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 2999);
		}});

		// Malevolence (359)
		sQuirkTable.put(59474, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 1795);
		}});

		// Mobus's Dripping Halberd
		sQuirkTable.put(69843, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 2431);
		}});

		// Vicious Gladiator's Staff (359)
		sQuirkTable.put(61343, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 1496);
		}});

		// Jin'do's Verdict
		sQuirkTable.put(69626, new HashMap<Property,Number>() {{
			put(Property.MELEE_DMG_MAX, 2334);
		}});
	}

	@Override
	public RawItem getById(int id) {
		try {
			URL url = new URL("http://www.wowhead.com/item=" + id + "&xml");
			String page = URLFetcher.getInstance().fetchAsString(url, true);

			SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(new StringReader(page));
			Element root = doc.getRootElement();
			Element element = root.getChild("item");

			RawItem item = new RawItem(id, element.getChildText("name"));

			item.properties.put(Property.QUALITY, Integer.parseInt(element.getChild("quality").getAttributeValue("id")));
			item.properties.put(Property.CLASS, Integer.parseInt(element.getChild("class").getAttributeValue("id")));
			item.properties.put(Property.SUBCLASS, Integer.parseInt(element.getChild("subclass").getAttributeValue("id")));
			item.icon = element.getChildText("icon").toLowerCase();

			// Check for socket bonus: <span class="q0">Socket Bonus: +8 Agility</span>
			String htmlTooltip = element.getChildText("htmlTooltip");
		    Matcher m = Pattern.compile("<span class=\"q0\">Socket Bonus: \\+(\\d+) (.+?)</span>").matcher(htmlTooltip);
		    if (m.find()) {
				int bonus = Integer.parseInt(m.group(1));
				String stat = m.group(2);
				if (sSocketMap.containsKey(stat)) {
					item.properties.put(Property.SOCKET_BONUS_STAT, sSocketMap.get(stat).ordinal());
					item.properties.put(Property.SOCKET_BONUS_VALUE, bonus);
				}
				else System.out.println("** WowheadItemSource: Socket Bonus \"" + stat + "\" not yet supported");
			}

			JSONObject jso = (JSONObject)JSONValue.parse("{" + element.getChildText("jsonEquip") + "}");
			@SuppressWarnings("unchecked") Set<Map.Entry<Object,Object>> set = jso.entrySet();
			for (Map.Entry<Object,Object> i : set) {
				String k = i.getKey().toString();
				Number n = (Number)i.getValue();
				if (sPropertyMap.containsKey(k)) {
					Property p = sPropertyMap.get(k);
					if (p != null) {
						if (p == Property.MELEE_SPEED || p == Property.MELEE_DPS || p == Property.RANGED_SPEED || p == Property.RANGED_DPS) {
							item.properties.put(p, n.doubleValue());
						} else {
							item.properties.put(p, n.intValue());
						}
					}
				}
				else System.out.println("** WowheadItemSource: Property " + k + " not yet supported");
			}

		    // Check for bonus armor: <!--addamr82-->
		    m = Pattern.compile("<!--addamr(\\d+)-->").matcher(page);
		    if (m.find()) {
		    	int bonusArmor = Integer.parseInt(m.group(1));
		    	item.properties.put(Property.BONUS_ARMOR, bonusArmor);
		    	int armor = item.properties.get(Property.ARMOR).intValue() - bonusArmor;
		    	item.properties.put(Property.ARMOR, armor);
		    }

			if (sQuirkTable.containsKey(item.id)) {
				System.out.println(" ** WowheadItemSource: Loading quirks for item " + item.id);
				for (Map.Entry<Property, Number> quirk : sQuirkTable.get(item.id).entrySet()) {
					System.out.println(" **  " + quirk.getKey() + " is " + item.properties.get(quirk.getKey()) + " should be " + quirk.getValue());
					item.properties.put(quirk.getKey(), quirk.getValue());
				}
			}
			
			return item;
		}
		catch (Exception e) {
			sExceptions.append(e.toString());
			if (mNext != null) return mNext.getById(id);
			else return null;
		}
	}
}
